/*
 *  Copyright 2013 Utkin Dmitry
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include <google/protobuf/descriptor.h>
#include <protoqtrpc/BlockingClosure.h>
#include <protoqtrpc/RpcController.h>
#include "protoqtrpc/location/LocationService.pb.h"
#include "LocationRpcChannel.h"
#include "LocationServiceClient.h"

namespace protoqtrpc {
namespace location {

LocationRpcChannel::LocationRpcChannel():
    mClient(NULL)
{
}

LocationRpcChannel::LocationRpcChannel(const ::google::protobuf::ServiceDescriptor* descriptor,
                                       const QHostAddress& serviceHost,
                                       const QString& serverName,
                                       const QHostAddress& locationServiceHost,
                                       quint16 locationServicePort):
    mClient(NULL)
{
    connectToService(descriptor, serviceHost, serverName, locationServiceHost,
                     locationServicePort);
}

LocationRpcChannel::~LocationRpcChannel()
{
}

bool LocationRpcChannel::connectToService(const ::google::protobuf::ServiceDescriptor* descriptor,
                                          const QHostAddress& serverHost,
                                          const QString& serverName,
                                          const QHostAddress& locationServiceHost,
                                          quint16 locationServicePort)
{
    Q_ASSERT(descriptor);

    if (isConnectedToService())
    {
        qWarning() << "Already connected to a service [" << descriptor->full_name().c_str() << "]";
        return true;
    }

    if (!mClient ||
            mClient->serverHost() != locationServiceHost ||
            mClient->serverPort() != locationServicePort)
    {
        delete mClient;
        mClient = new LocationServiceClient(locationServiceHost, locationServicePort);
    }

    QList<LocationServiceClient::ServiceLocation> locations;
    LocationServiceClient::Status status =
            mClient->locateService(QString::fromUtf8(descriptor->full_name().c_str()), locations);

    if (status != LocationServiceClient::STATUS_SUCCESS)
    {
        return false;
    }

    // find our service in list
    const LocationServiceClient::ServiceLocation* foundLocation = NULL;
    foreach (const LocationServiceClient::ServiceLocation& location, locations)
    {
        if ((serverName.isEmpty() || serverName == location.serverInfo.name) &&
                ((serverHost == QHostAddress::Any) || serverHost == location.serverInfo.host))
        {
            foundLocation = &location;
            break;
        }
    }

    if (!foundLocation)
    {
        qWarning() << "Failed to find a service [" << descriptor->full_name().c_str()
                   << "]: No matching service found.";
        return false;
    }

    if (!SocketRpcChannel::connectToService(foundLocation->serverInfo.host,
                                            foundLocation->serverInfo.port))
    {
        qWarning() << "Failed to connect to a service [" << descriptor->full_name().c_str()
                   << "]: started on " << QHostAddress(foundLocation->serverInfo.host) << ":"
                   << foundLocation->serverInfo.port;
        return false;
    }

    return true;
}

}
}
